﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Linq;
namespace UmlControlLibrary
{
    public class UmlElementEventArgs : CancelEventArgs
    {
        private UmlElement _sender;
        public UmlElement Sender
        {
            get { return _sender; }
        }

        public UmlElementEventArgs(UmlElement sender)
            : base()
        {
            _sender = sender;
        }
    }

    public class UmlElementPropertyChangedEventArgs : UmlElementEventArgs
    {
        private string _propertyName;
        public string PropertyName
        {
            get { return _propertyName; }
        }

        private object _oldValue;
        public object OldValue
        {
            get { return _oldValue; }
        }

        public object NewValue
        {
            get;
            set;
        }

        public UmlElementPropertyChangedEventArgs(UmlElement sender, string propertyName, object oldvalue, object newvalue)
            : base(sender)
        {
            _propertyName = propertyName;
            _oldValue = oldvalue;
            NewValue = newvalue;
        }
    }

    public delegate void UmlElementPropertyChangedDelegate(UmlElementPropertyChangedEventArgs args);
    public delegate void UmlElementAddedDelegate(UmlElementEventArgs args);


    public class UmlElement : DependencyObject, INotifyPropertyChanged
    {
        public class Events
        {
            public static Event Created = new Event(Event.Created, typeof(UmlElement));
            public static Event Modified = new Event(Event.Modified, typeof(UmlElement));
            public static Event Deleted = new Event(Event.Deleted, typeof(UmlElement));
        }

        public event UmlElementPropertyChangedDelegate UmlElementPropertyChanged;

        public object OnUmlElementPropertyChanged(string PropertyName, object oldvalue, object newvalue)
        {
            // If delegate is assigned
            if (UmlElementPropertyChanged != null)
            {
                // Build args
                UmlElementPropertyChangedEventArgs args = new UmlElementPropertyChangedEventArgs(this, PropertyName, oldvalue, newvalue);
                // Call delegate
                UmlElementPropertyChanged(args);
                // If change wasn't canceled, newvalue may have changed
                if (!args.Cancel)
                {
                    return args.NewValue;
                }
                else
                    // if change was canceled, return the old value
                    return oldvalue;
            }
            else
                // else return the new value
                return newvalue;
        }

        public event UmlElementAddedDelegate UmlElementAdded;

        private ObservableCollection<UmlElement> _elements = new ObservableCollection<UmlElement>();
        public ObservableCollection<UmlElement> Elements
        {
            get { return _elements; }
            set { _elements = value; }
        }

        private string _name;
        public virtual string Name
        {
            get { return _name; }
            set
            {
                if (value.Length > 0)
                {
                    _name = (string)OnUmlElementPropertyChanged("Name", _name, value);
                    OnPropertyChanged("Name");
                }
            }
        }

        public string Guid
        {
            get;
            set;
        }

        public UmlMetadata Metadata
        {
            get;
            set;
        }

        public bool Locked
        {
            get;
            set;
        }

        public bool Unlockable
        {
            get;
            set;
        }

        public UmlElement()
        {
            this.Guid = System.Guid.NewGuid().ToString();
            this.Metadata = new UmlMetadata();
            Locked = false;
            Unlockable = true;
        }

        public UmlElement(string name)
            : this()
        {
            this.Name = name;
            EventService.BroadcastEvent(this, UmlElement.Events.Created);
        }

        public virtual ObservableCollection<UmlElement> GetChilds()
        {
            ObservableCollection<UmlElement> elements = new ObservableCollection<UmlElement>();
            foreach (UmlElement element in Elements)
                elements.Add(element);
/*            foreach (UmlElement element in UmlElementList.instance.GetRelationsFor(this))
                elements.Add(element);*/
            return elements;
        }

        public virtual bool OnElementAdded(UmlElement element)
        {
            // If delegate is assigned
            if (UmlElementAdded != null)
            {
                // Build args
                UmlElementEventArgs args = new UmlElementEventArgs(element);
                // Call delegate
                UmlElementAdded(args);
                if (!args.Cancel)
                {
                    Elements.Add(element);
                    OnPropertyChanged("Childs");
                    return true;
                }
            }
            return true;
        }

        public ObservableCollection<UmlElement> Childs
        {
            get { return GetChilds(); }
        }

        public virtual void AddElement(UmlElement element)
        {
            if (OnElementAdded(element))
                UmlModel.AddElement(element);
                //UmlElementList.Instance.AddElement(element);
        }

        public virtual UmlElement GetElementByName(string name)
        {
            UmlElement result = Elements.FirstOrDefault(e => e.Name.ToUpper() == name.ToUpper());
            return result;
        }

        public virtual UmlElement GetElementByGuid(string guid)
        {
            UmlElement result = Elements.FirstOrDefault(e => e.Guid == guid);
            return result;
        }

        public IEnumerable<UmlElement> GetRelationsFor(UmlElement element)
        {
            IEnumerable<UmlElement> elements = Elements.Where(e => (e is UmlRelation) && (((UmlRelation)e).End1.EndElement == element));
            return elements;
        }

        public IEnumerable<UmlElement> GetRelationsFor(UmlElement end1, UmlElement end2)
        {
            IEnumerable<UmlElement> elements = Elements.Where(e => (e is UmlRelation) && (((UmlRelation)e).End1.EndElement == end1) && (((UmlRelation)e).End2.EndElement == end2));
            return elements;
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
    }
}
